#include "iso9660.h"

static BOOLEAN iso9660_init(struct super_block * sb, dev_t dev);
static void iso9660_free(struct super_block * sb);
static BOOLEAN iso9660_iread(struct inode * inoptr, dev_t dev, ino_t ino);
static BOOLEAN iso9660_iwrite(struct inode * inoptr, dev_t dev, ino_t ino);
static BOOLEAN iso9660_ialloc(
	struct super_block * sb, struct inode * inoptr, mode_t mode
	);
static void iso9660_ifree(struct super_block * sb, ino_t ino);
static unsigned long iso9660_balloc(struct super_block * sb);
static void iso9660_bfree(struct super_block * sb, unsigned long block);
static int iso9660_statfs(struct super_block * sb, struct statfs * stat);

struct filesystem iso9660_filesystem = {
	&iso9660_init,
	&iso9660_free,
	&iso9660_iread,
	&iso9660_iwrite,
	&iso9660_ialloc,
	&iso9660_ifree,
	&iso9660_balloc,
	&iso9660_bfree,
	&iso9660_statfs,
};

int module_enter(void)
{
	printk("load filesystem iso9660 ");

	if(register_filesystem(&iso9660_filesystem))
	{
		printk("successed\n");

		return 0;
	}
	else
	{
		printk("failed\n");

		return -1;
	}

	return 1;
}

int module_exit(void)
{
	BOOLEAN ret;
	
	printk("unload filesystem iso9660 ");

	if ((ret = unregister_filesystem(&iso9660_filesystem)))
	{
		printk("successful\n");

		return 0;
	}
	else
	{
		printk("failed\n");
		return -1;
	}
}

static BOOLEAN iso9660_init(struct super_block * sb, dev_t dev)
{
	struct iso9660_super_block * isb;

	isb = (struct iso9660_super_block *)
		kmalloc(sizeof(struct iso9660_super_block), PageWait);
	if(!isb){
		PRINTK("out of memory\n");
		return FALSE;
	}

	if(!ide_cdrom_open(MINOR(dev), isb)){
		kfree(isb);
		return FALSE;
	}

	sb->s_filesystem = &iso9660_filesystem;
	sb->s_data = isb;

	sb->s_roflag = 1;

	return TRUE;
}

static void iso9660_free(struct super_block * sb)
{
	// xxx
	if (sb->s_data)
	{
		kfree(sb->s_data);
	}
	sb->s_data = NULL;
}

extern struct inode_operation iso9660_inode_operation;

static void fill_iso9660_inode(
	struct iso9660_inode * iinoptr, 
	struct iso_directory_record * rec
	)
{
	bios_time_t t;
	unsigned long ult;

	if(ISO_ISDIR(rec->flags))
		iinoptr->i_mode = 0755 | I_DIR;
	else
		iinoptr->i_mode = 0644 | I_REG;

	iinoptr->i_nlinks = 1;
	iinoptr->i_uid = SU_UID;
	iinoptr->i_gid = SU_GID;
	iinoptr->i_size = rec->size;

	// ignore offset from Greenwich Mean Time

	t.second = rec->date[5];
	t.minute = rec->date[4];
	t.hour = rec->date[3];
	t.day = rec->date[2];
	t.month = rec->date[1];
	t.year = rec->date[0] % 100;
	t.century = 19 + rec->date[0] / 100;

	ult = time_to_second(&t);

	iinoptr->i_atime = ult;
	iinoptr->i_mtime = ult;
	iinoptr->i_ctime = ult;

	// xxx
	iinoptr->i_first_block = rec->first_sector;
	iinoptr->i_volume_sequence_number = rec->volume_sequence_number;
}

/*
 * inoptr is not locked
 */
static BOOLEAN iso9660_iread(struct inode * inoptr, dev_t dev, ino_t ino)
{
	struct super_block * sb;
	struct iso9660_super_block * isb;
	struct iso9660_inode * iinoptr;

	sb = inoptr->i_sb;
	if(!sb)
		DIE("BUG: cannot happen\n");

	iinoptr = (struct iso9660_inode *)
		kmalloc(sizeof(struct iso9660_inode), PageWait);
	if(!iinoptr){
		printk("out of memory\n");
		return FALSE;
	}

	isb = (struct iso9660_super_block *)sb->s_data;

	if(ino == ROOT_INODE)
		fill_iso9660_inode(iinoptr, &isb->root_directory_record);
	else{
		int offset;
		unsigned long block;
		struct buf_head * bh;
		struct iso_directory_record * rec;

		block = ino / CDROM_BLOCKSIZ;
		offset = ino % CDROM_BLOCKSIZ;

		bh = bread(dev, block);
		if(!bh){
			PRINTK("bread error\n");
			
			kfree(iinoptr);
			return FALSE;
		}

		rec = (struct iso_directory_record *)&bh->b_data[offset];
		fill_iso9660_inode(iinoptr, rec);

		brelse(bh);
	}

	iinoptr->i_isb = isb;

	inoptr->i_ops = &iso9660_inode_operation;
	inoptr->i_data = iinoptr;

	return TRUE;
}

static BOOLEAN iso9660_iwrite(struct inode * inoptr, dev_t dev, ino_t ino)
{
	return TRUE;
}

static BOOLEAN iso9660_ialloc(
	struct super_block * sb, struct inode * inoptr, mode_t mode
	)
{
	printk("iso9660_ialloc\n");
	return FALSE;
}

static void iso9660_ifree(struct super_block * sb, ino_t ino)
{
	printk("iso9660_ifree\n");
}

static unsigned long iso9660_balloc(struct super_block * sb)
{
	printk("iso9660_balloc\n");
	return FALSE;
}

static void iso9660_bfree(struct super_block * sb, unsigned long block)
{
	printk("iso9660_bfree\n");
}

static int iso9660_statfs(struct super_block * sb, struct statfs * stat)
{
	// xxx
	printk("iso9660_statfs\n");
	return 0;
}
